[JS Behind The Scene] prototype chain-new 在背後到底做了甚麼?


Posted by Powerfultraveling 's Blog on 2021-08-22

前言

本周進入到原生 JavaScript 背後的運作模式,其中比較複雜的是原型鍊(prototype chain)這一部分,所已整理了一系列的筆記在這邊整理一下腦中的思路。

而這一篇的主題是 new 在背後做了甚麼,主要在整理 huli 老師在課堂中講解 new 的邏輯推演,從新自己寫了一個相似的例子,順過一遍,適合 prototype 新手閱讀。

new 在背後到底做了甚麼?

先看一個常見的例子

//設立一個 constructor function 
function Stuff(name, job){
    this.name = name;
    this.job = job;
}

//丟一個 method 到 Stuff 的 prototype裡
Stuff.prototype.log = function(){
    console.log(this.name + " " + this.job);
}

//設立兩個 Stuff 的 instance 
let james = new Staff("james", "doctor");
let amy = new Staff("amy", "designer");

console.log(james)//{name:"james", job:"doctor"}
console.log(amy)//{name:"amy", job:"designer} 

james.log()//"james" "doctor"
amy.log()//"amy"  "designer"

從這邊可已發現,兩個 instance 雖然自己沒有 log 這個 method,但卻有辦法依著 prototype chain 呼叫到放在 Stuff.prototye 裡面的 function。

究竟 new 在背後做了甚麼呢? 讓他們可以這樣運行呢?

其實一點也不複雜,huli 老師在課程中實作了一個 function,讓我們了解 new 在背後的運作機制,在這邊想要遵循這個思路自己建造一個 function,一步步推演 new 的運行機制。

在實作之前,先看看new做出來的東西要有那些特質:

  1. 一個物件。
  2. 物件裡面的屬性是根據 Staff() 裡面的設定(就是有 name 和 work 倆的個屬性啦。)
  3. 這個物件能夠用到 Staff.prototype 裡的 method。

根據以上的 requirement,我寫了以下這段程式:

function Stuff(name, job){
    this.name = name;
    this.job = job;
}

Stuff.prototype.func = function(){
    console.log(this.job)
}

function newFake(name, job){
    let obj = {};
    Stuff.call(obj, name, job);
    obj.__proto__ = Stuff.prototype;
    return obj
}

let charles = newFake("charles", "student");
console.log(charles);//{name:"charles", job:"student"}

仔細一看,其實這個函式的結構很簡單。

  1. 設定一個空 object,準備用於在未來接收值。
  2. 用 call() 來呼叫 Stuff,Staff 接受收了 name 和 age 兩個參數,建立 instance。

---------------- 哀不對怪怪的

Staff 明明只有兩個參數,為甚麼 call() 裡面多了一個參數,而且那個參數的值還是樓上的 obj。

這是因為如果要創建一個新的 instance 的話,我們必須要指定好 this 指向的對象,說起來,如果連可以連接的對象都沒有的話,怎麼可能可以創造出一個 instance 呢?

有了前面的認知之後,我們現在的目標很明確,就是將 Stuff() 裡的 this 指向我們未來要創造的 instance,在這邊就是 newFake 裡面的 obj。那我們要怎麼做呢?

這時候就輪到 call() method 登場啦!

詳細的介紹大家可以看這裡,這邊先丟上 MDN 對 call() method 簡單的定義:

The call() method calls a function with a given this value and arguments provided individually.

從這段定義我們可以得到以下幾個資訊:

  1. call()可以用來呼叫函數。
  2. call()可以傳進該函數所需要的參數。
  3. 不單如此,call()還可以傳進 this 所指向的對象。

換言之,我們可以利用 call() method 將我們想指定的 this 對象傳進去,也就可以達成我們的目標了,但是,要麼做呢?

文件在往下拉,裡面介紹了 call() 的 syntax:

call()
call(thisArg)
call(thisArg, arg1)
call(thisArg, arg1, arg2)
call(thisArg, arg1, ... , argN)

原來 call()method 的第一為參數就是留給 this 的對象的。看到這裡,我想這一段這樣寫的原因就很清楚了:

Staff.call(obj, name, job)

第一個參數是 this 要指向的對象,後面兩個則是 Staff 原本的兩個參數。這段完成之後就建構出一個根據 Staffinstance 了!

最後還有一個問題要解決,要怎麼讓新創見的 obj 也能夠用到 Staff.prototype 裡的 method 呢? 答案是將 obj 的 proto 屬性連接上 Staff.prototype。

所以最後還得加上這一段,連接兩者才是真正建構出一個根據 Staff 的 instance。

obj.__proto__ = Staff.prototype;

總結

最後在這邊用條列式,一行行總結一下 new 的背後機制吧:

  1. 設立一個空 object 準備在未來放東西進去。

  2. 利用 constructor function 將東西放進去空 object,而且 object 的結構長得跟 constructor function 定義的一樣。

  3. 將 object 的 proto 屬性連結上 constructor function 的 prototype,這麼一來,這個 object 不只有 constructor function 鎖定一個結構,還可以呼叫到在 constructor function 的 prototype 裡的 method。(這段有點饒口,有空回來修)

  4. 將這個設定好的 object 回傳。


#new #class #prototype







Related Posts

mkcert:讓你在 localhost 開發也能用 https

mkcert:讓你在 localhost 開發也能用 https

理解與解決 N + 1 problem

理解與解決 N + 1 problem

矽谷思維

矽谷思維


Comments